# @Time : 2020/6/25 11:52
# @Author : Fioman 
# @Phone : 13149920693
"""
有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值.
有两个途径能帮我们达到这个目的,一个是通过defaultdict这个类型而不是普通的dict,另一个是给自己定义一个dict的子类,
然后在子类中实现__missing__方法.
"""

"""
使用defaultdict处理找不到键的时候的情况
具体而言,在实例化一个defaultdict的时候,需要给构造方法提供一个可调用的对象,这个可调用的对象会在__getitem__碰到找不到键的时候被调用.
让__getitem__返回某种默认值.
比如:我们新建了一个字典:dd = defaultdict(list),如果键'new_key'在dd中还不存在的话,表达式dd["new_key"]会按照以下的步骤来行事.
1> 调用list()来建立一个新的列表
2> 把这个新的列表作为值,'new_key'作为它的键,放到dd中.
3> 返回这个列表的引用.
"""

import sys
import re
import collections

WORD_RE = re.compile(r"\w+")
index = collections.defaultdict(list)

with open("test.txt", encoding='utf-8') as fp:
    for line_no, line in enumerate(fp, 1):
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            index[word].append(location)

for word in sorted(index, key=str.upper):
    print(word, index[word])

# 把list构造方法作为default_factory来创建一个defaultdict.
# 如果index没有word记录,那么default_factory会被调用,为查询不到的键创造一个值.这个值在这里是一个空列表,
# 然后这个空列表被赋值给index[word],继而被当作返回值返回,因此.append(location)操作总能成功.

"""
defaultdict里的default_factory只会在__getitem__里被调用,在其他的方法里完全不会发挥作用.
比如,dd是个defaultdict,k是找不到的键,dd[k]这个表达式会调用default_factory创建某个默认值,而dd.get(k)则会返回None.
所有这一切背后的功臣其实是特殊方法__missing__,它会在defaultdict遇到找不到键的时候调用default_factory
特殊方法__missing__
__missing__方法只会被__getitem__调用(比如在表达式d[k]中).提供__missing__方法对get或者__contains__(in 云算法会用到这个方法.)
这些方法没有影响.
把非字符串的键转换为字符串.
"""


class StrKeyDict0(dict):

    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError
        return self[str[key]]

    def get(self,key,default=None):
        try:
            return self[key]
        except KeyError:
            return default

    def __contains__(self, key):
        return key in self.keys() or str(key) in self.keys()

"""
1> StrKeyDict0 继承了dict
2> 如果找不到键本身就是字符串,那就抛出KeyError异常
3> 如果找不到的键不是字符串,那么把它转换成字符串再进行查找
4> get方法把查找工作用self[key]的形式委托给__getitem__,这样在宣布查找失败之前,还能通过__missing__再给某个键一个机会.
5> 如果抛出KeyError,那么说明__missing__也失败了,于是返回default
6> 先按照传入键的原本的值来查找(我们映射类型中可能含有非字符串的键),如果没有找到,再用str()方法把键转换成字符串再查找一次.
"""

































